/*
 * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "driver/chip/hal_prcm.h"

#include "hal_base.h"


#if (CONFIG_CHIP_ARCH_VER == 2)

#elif (CONFIG_CHIP_ARCH_VER == 3)

void HAL_PRCM_EnableSMPSPwmSel(uint32_t enable)
{
	if (enable)
		HAL_SET_BIT(PRCM->SYS_TOP_LDO_CTRL, PRCM_SMPS_PWM_SEL_BIT);
	else
		HAL_CLR_BIT(PRCM->SYS_TOP_LDO_CTRL, PRCM_SMPS_PWM_SEL_BIT);
}

void HAL_PRCM_SetSMPSSwTrim(uint32_t sw_trim)
{
	HAL_MODIFY_REG(PRCM->SYS_LDO_SW_CTRL, PRCM_EXT_LDO_SW_TRIM_MASK, sw_trim << PRCM_EXT_LDO_SW_TRIM_SHIFT);
}

void HAL_PRCM_EnalbeEXTLDOSwMode(uint32_t enable)
{
	if (enable)
		HAL_SET_BIT(PRCM->SYS_LDO_SW_CTRL, PRCM_EXT_LDO_SW_MODE_BIT);
	else
		HAL_CLR_BIT(PRCM->SYS_LDO_SW_CTRL, PRCM_EXT_LDO_SW_MODE_BIT);
}

void HAL_PRCM_SetDcxoFreqTrim(uint32_t freq_trim)
{
	HAL_MODIFY_REG(PRCM->DCXO_CTRL, PRCM_FREQ_OFFSET_MASK, (freq_trim << PRCM_FREQ_OFFSET_SHIFT) & PRCM_FREQ_OFFSET_MASK);
}

uint32_t HAL_PRCM_GetBLERstStatus(void)
{
	return !!HAL_GET_BIT(PRCM->SYS1_RST_STATUS, PRCM_BLE_RESET_BIT);
}

uint32_t HAL_PRCM_GetSys1SramSleepPowerFlags(void)
{
	return HAL_GET_BIT(PRCM->SYS1_SLEEP_CTRL, PRCM_SYS_SRAM_PWR_CTRL_MASK);
}

void HAL_PRCM_SetSys1SramSleepPowerFlags(uint32_t flags)
{
	HAL_MODIFY_REG(PRCM->SYS1_SLEEP_CTRL, PRCM_SYS_SRAM_PWR_CTRL_MASK, flags & PRCM_SYS_SRAM_PWR_CTRL_MASK);
}

void HAL_PRCM_SetWlanSramShare(PRCM_WLAN_ShareSramType type)
{
	HAL_MODIFY_REG(PRCM->WLAN_SRAM_SHARE_CTRL, PRCM_WLAN_SHARE_SRAM_MASK, type);
}

void HAL_PRCM_SetRTCLDOVoltage(PRCM_RTCLDOVolt volt)
{
	HAL_MODIFY_REG(PRCM->RTC_LDO_VOLT_CTRL, PRCM_RTC_LDO_VOLT_MASK, volt);
}

#ifdef CONFIG_ROM
void HAL_PRCM_SetWakeupIOxDebounce(uint8_t ioIndex, uint8_t val)
{
	HAL_ASSERT_PARAM(ioIndex < WAKEUP_IO_MAX);

	if (ioIndex < 8) {
		HAL_MODIFY_REG(PRCM->CPUA_WAKE_IO_DEB_CYCLES0,
		               PRCM_WAKEUP_IO0T7_DEDOUNCE_CYCLE_MASK(ioIndex),
		               val << PRCM_WAKEUP_IO0T7_DEDOUNCE_CYCLE_SHIFT(ioIndex));
	} else if (ioIndex < WAKEUP_IO_MAX) {
		ioIndex -= 8;
		HAL_MODIFY_REG(PRCM->CPUA_WAKE_IO_DEB_CYCLES1,
		               PRCM_WAKEUP_IO8T13_DEDOUNCE_CYCLE_MASK(ioIndex),
		               val << PRCM_WAKEUP_IO8T13_DEDOUNCE_CYCLE_SHIFT(ioIndex));
	}
}
#endif

void HAL_PRCM_SetBLEClk32KSel(PRCM_BLE_CLK32K_Sel sel)
{
	HAL_MODIFY_REG(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_SEL_MASK, sel);
}

PRCM_BLE_CLK32K_Sel HAL_PRCM_GetBLEClk32KSel(void)
{
	return HAL_GET_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_SEL_MASK);
}

void HAL_PRCM_SetRcoscCalStartSrc(PRCM_RcoscCalStartSrc src)
{
	HAL_MODIFY_REG(PRCM->POWERCTRL_CFG, PRCM_RCOSC_CAL_START_SRC_MASK, src);
}

PRCM_RcoscCalStartSrc HAL_PRCM_GetRcoscCalStartSrc(void)
{
	return HAL_GET_BIT(PRCM->POWERCTRL_CFG, PRCM_RCOSC_CAL_START_SRC_MASK);
}

uint32_t HAL_PRCM_GetRcoscCalSwitchLoseClk(void)
{
	uint32_t loss_clk;
	uint32_t src_clock;

	if (HAL_GET_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_DIV_SRC_SEL_MASK) == PRCM_BLE_CLK32K_DIV_SRC_SEL_HFCLK) {
		src_clock = HAL_PRCM_GetHFClock();
	} else {
		src_clock = 32000000; /* 32MHz */
	}
	loss_clk = HAL_GET_BIT_VAL(PRCM->BLE_CLK32K_SWITCH1, PRCM_BLE_CLK32K_SW_OFFSET_DOWN_SHIFT,
	                           PRCM_BLE_CLK32K_SW_OFFSET_DOWN_VMASK);
	loss_clk += HAL_GET_BIT_VAL(PRCM->BLE_CLK32K_SWITCH1, PRCM_BLE_CLK32K_SW_OFFSET_ON_SHIFT,
	                            PRCM_BLE_CLK32K_SW_OFFSET_ON_VMASK);
	loss_clk /= (src_clock / 1000000);

	return loss_clk;
}

void HAL_PRCM_SetCLK32KSwitchRCCal(uint8_t enable)
{
	if (enable) {
		HAL_SET_BIT(PRCM->SRAM_BIST_CTRL, PRCM_CLK32K_SW_FORCE_EN_BIT | PRCM_CLK32K_SW_FORCE_DOWN_BIT |
		            PRCM_CLK32K_SW_FORCE_READY_BIT);
		HAL_UDelay(100);
		HAL_MODIFY_REG(PRCM->SRAM_BIST_CTRL,
		               PRCM_CLK32K_SW_FORCE_EN_BIT | PRCM_CLK32K_SW_FORCE_DOWN_BIT | PRCM_CLK32K_SW_FORCE_READY_BIT,
		               PRCM_CLK32K_SW_FORCE_EN_BIT & (~PRCM_CLK32K_SW_FORCE_READY_BIT) & (~PRCM_CLK32K_SW_FORCE_DOWN_BIT));
	} else {
		HAL_MODIFY_REG(PRCM->SRAM_BIST_CTRL,
		               PRCM_CLK32K_SW_FORCE_EN_BIT | PRCM_CLK32K_SW_FORCE_DOWN_BIT | PRCM_CLK32K_SW_FORCE_READY_BIT,
		               PRCM_CLK32K_SW_FORCE_EN_BIT | PRCM_CLK32K_SW_FORCE_READY_BIT);
		HAL_UDelay(100);
	}
}

void HAL_PRCM_DisableVddioSipSw(void)
{
	HAL_CLR_BIT(PRCM->SYS1_WAKEUP_CTRL, PRCM_VDDIO_SIP_SW_EN_BIT);
}

void HAL_PRCM_EnableVddioSipSw(void)
{
	HAL_SET_BIT(PRCM->SYS1_WAKEUP_CTRL, PRCM_VDDIO_SIP_SW_EN_BIT);
}

int HAL_PRCM_IsVddioSipSwEnable(void)
{
	return HAL_GET_BIT(PRCM->SYS1_WAKEUP_CTRL, PRCM_VDDIO_SIP_SW_EN_BIT);
}

/*
 *fix that inter32kfreq regs is 0 before 24M clock complete checking the inter32k clock.
 *32768 is recommended value.
 */
PRCM_LFClkSrc HAL_PRCM_GetBaseLFClockType(void)
{
	uint32_t val = HAL_GET_BIT(PRCM->SYS_LFCLK_CTRL, PRCM_LFCLK_BASE_SRC_MASK);

	if (val == PRCM_LFCLK_BASE_SRC_INTER32K &&
	    HAL_GET_BIT(PRCM->SYS_RCOSC_CALIB_CTRL, PRCM_RCOSC_CALIB_EN_BIT)) {
		if (!HAL_PRCM_GetInter32KFreq()) {
			return PRCM_LFCLK_SRC_LOSC;
		} else {
			return PRCM_LFCLK_SRC_RCOSC;
		}
	} else {
		return PRCM_LFCLK_SRC_LOSC;
	}
}

#ifdef CONFIG_ROM
void HAL_PRCM_SetLFCLKSource(PRCM_LFClkModule module, PRCM_LFClkSrc src)
{
	switch (module) {
	case PRCM_LFCLK_MODULE_SYS:
		if (src == PRCM_LFCLK_SRC_RCCAL) {
			HAL_SET_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_SYS_CLK32K_RCO_CALIB_SEL_BIT);
		} else {
			HAL_CLR_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_SYS_CLK32K_RCO_CALIB_SEL_BIT);
		}
		break;
	case PRCM_LFCLK_MODULE_WKTIMER:
		if (src == PRCM_LFCLK_SRC_RCCAL) {
			HAL_SET_BIT(PRCM->CLK_32K_CTRL, PRCM_WKTIMER_32K_CLK_RCO_CALIB);
		} else {
			HAL_CLR_BIT(PRCM->CLK_32K_CTRL, PRCM_WKTIMER_32K_CLK_RCO_CALIB);
		}
		break;
	case PRCM_LFCLK_MODULE_WLAN:
		if (src == PRCM_LFCLK_SRC_RCCAL) {
			HAL_SET_BIT(PRCM->CLK_32K_CTRL, PRCM_WLAN_32K_CLK_RCO_CALIB);
		} else {
			HAL_CLR_BIT(PRCM->CLK_32K_CTRL, PRCM_WLAN_32K_CLK_RCO_CALIB);
		}
		break;
	case PRCM_LFCLK_MODULE_BLE:
		HAL_CLR_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_AUTO_SW_EN_BIT); /* disable hosc 32K*/
		if (src == PRCM_LFCLK_SRC_RCCAL) {
			HAL_MODIFY_REG(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_SEL_MASK,
			               PRCM_BLE_CLK32K_SEL_RCCAL);
		} else {
			HAL_MODIFY_REG(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_SEL_MASK,
			               PRCM_BLE_CLK32K_SEL_RCOSCORLFCLK);
		}
		break;
	case PRCM_LFCLK_MODULE_RTC:
		if (src == PRCM_LFCLK_SRC_RCCAL) {
			HAL_MODIFY_REG(PRCM->CLK_32K_CTRL, PRCM_RTC_32K_CLK_SOURCE_MASK, PRCM_RTC_32K_CLK_RCO_CALIB);
		} else if (src == PRCM_LFCLK_SRC_DIV32K) {
			HAL_MODIFY_REG(PRCM->CLK_32K_CTRL, PRCM_RTC_32K_CLK_SOURCE_MASK, PRCM_RTC_32K_CLK_HOSC_DIV32K);
		} else {
			HAL_MODIFY_REG(PRCM->CLK_32K_CTRL, PRCM_RTC_32K_CLK_SOURCE_MASK, PRCM_RTC_32K_CLK_RCO_LOSC);
		}
		break;
	default:
		break;
	}
}
#endif

PRCM_LFClkSrc HAL_PRCM_GetLFClockType(PRCM_LFClkModule module)
{
	uint32_t src;
	PRCM_LFClkSrc type = PRCM_LFCLK_SRC_MAX;

	switch (module) {
	case PRCM_LFCLK_MODULE_SYS:
		src = HAL_GET_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_SYS_CLK32K_RCO_CALIB_SEL_BIT);
		if (src == PRCM_SYS_CLK32K_RCO_CALIB_SEL_BIT) {
			type = PRCM_LFCLK_SRC_RCCAL;
		}
		break;
	case PRCM_LFCLK_MODULE_WKTIMER:
		src = HAL_GET_BIT(PRCM->CLK_32K_CTRL, PRCM_WKTIMER_32K_CLK_RCO_CALIB);
		if (src == PRCM_WKTIMER_32K_CLK_RCO_CALIB) {
			type = PRCM_LFCLK_SRC_RCCAL;
		}
		break;
	case PRCM_LFCLK_MODULE_WLAN:
		src = HAL_GET_BIT(PRCM->CLK_32K_CTRL, PRCM_WLAN_32K_CLK_RCO_CALIB);
		if (src == PRCM_WLAN_32K_CLK_RCO_CALIB) {
			type = PRCM_LFCLK_SRC_RCCAL;
		}
		break;
	case PRCM_LFCLK_MODULE_BLE:
		src = HAL_GET_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_SEL_RCCAL);
		if (src == PRCM_BLE_CLK32K_SEL_RCCAL) {
			type = PRCM_LFCLK_SRC_RCCAL;
		}
		break;
	case PRCM_LFCLK_MODULE_RTC:
		src = HAL_GET_BIT(PRCM->CLK_32K_CTRL, PRCM_RTC_32K_CLK_SOURCE_MASK);
		if ((src == PRCM_RTC_32K_CLK_RCO_CALIB) || (src == PRCM_RTC_32K_CLK_HOSC_DIV32K)) {
			type = PRCM_LFCLK_SRC_RCCAL;
		}
		break;
	case PRCM_LFCLK_MODULE_MAX:
		break;
	default:
		break;
	}

	if (type == PRCM_LFCLK_SRC_MAX)
		type = HAL_PRCM_GetBaseLFClockType();

	return type;
}

void HAL_PRCM_SetLDOTOPWMSTABLE_TIME(uint32_t time)
{
	HAL_MODIFY_REG(PRCM->DCDC_STABLE_REF_TIME, PRCM_LDOTOPWM_STABLE_RFE_TIME_VMASK,
	               time << PRCM_LDOTOPWM_STABLE_RFE_TIME_SHIFT);
}

uint32_t HAL_PRCM_GetLDOTOPWMSTABLE_TIME(void)
{
	return ((PRCM->DCDC_STABLE_REF_TIME & PRCM_LDOTOPWM_STABLE_RFE_TIME_VMASK) >> PRCM_LDOTOPWM_STABLE_RFE_TIME_SHIFT);
}

#ifdef CONFIG_ROM
void HAL_PRCM_SetDCDCSTABLE_TIME(uint32_t time)
{
	HAL_MODIFY_REG(PRCM->DCDC_STABLE_REF_TIME, PRCM_DCDC_STABLE_REF_TIME_VMASK, time << PRCM_DCDC_STABLE_RFE_TIME_SHIFT);
}

uint32_t HAL_PRCM_GetDCDCSTABLE_TIME(void)
{
	return ((PRCM->DCDC_STABLE_REF_TIME & PRCM_DCDC_STABLE_REF_TIME_VMASK) >> PRCM_DCDC_STABLE_RFE_TIME_SHIFT);
}
#endif

void HAL_PRCM_SetRFIPLDODIGSTABLE_TIME(uint32_t time)
{
	HAL_MODIFY_REG(PRCM->LDO_STABLE_REF_TIME, PRCM_RFIP_LDO_DIG_STABLE_REF_TIME_VMASK,
	               time << PRCM_RFIP_LDO_DIG_STABLE_RFE_TIME_SHIFT);
}

uint32_t HAL_PRCM_GetRFIPLDODIGSTABLE_TIME(void)
{
	return ((PRCM->LDO_STABLE_REF_TIME & PRCM_RFIP_LDO_DIG_STABLE_REF_TIME_VMASK) >>
	        PRCM_RFIP_LDO_DIG_STABLE_RFE_TIME_SHIFT);
}

void HAL_PRCM_SetLDOSTABLE_TIME(uint32_t time)
{
	HAL_MODIFY_REG(PRCM->LDO_STABLE_REF_TIME, PRCM_LDO_STABLE_REF_TIME_VMASK, time << PRCM_LDO_STABLE_RFE_TIME_SHIFT);
}

uint32_t HAL_PRCM_GetLDOSTABLE_TIME(void)
{
	return (PRCM->LDO_STABLE_REF_TIME & (PRCM_LDO_STABLE_REF_TIME_VMASK >> PRCM_LDO_STABLE_RFE_TIME_SHIFT));
}

void HAL_PRCM_SetDCXOSTABLE_TIME(uint32_t time)
{
	PRCM->DCXO_STABLE_REF_TIME = (time & PRCM_DCXO_STABLE_RFE_TIME_VMASK) << PRCM_DCXO_STABLE_RFE_TIME_SHIFT;
}

uint32_t HAL_PRCM_GetDCXOSTABLE_TIME(void)
{
	return (PRCM->DCXO_STABLE_REF_TIME & (PRCM_DCXO_STABLE_RFE_TIME_VMASK >> PRCM_DCXO_STABLE_RFE_TIME_SHIFT));
}


void HAL_PRCM_SetDPLLSTABLE_TIME(uint32_t time)
{
	PRCM->DPLL_STABLE_REF_TIME = (time & PRCM_DPLL_STABLE_RFE_TIME_VMASK) << PRCM_DPLL_STABLE_RFE_TIME_SHIFT;
}

uint32_t HAL_PRCM_GetDPLLSTABLE_TIME(void)
{
	return (PRCM->DPLL_STABLE_REF_TIME & (PRCM_DPLL_STABLE_RFE_TIME_VMASK >> PRCM_DPLL_STABLE_RFE_TIME_SHIFT));
}

uint32_t HAL_PRCM_GetDigSWRefTime(void)
{
	return ((PRCM->DIG_SWITCH_REF_TIME & PRCM_RESET_UP_REF_TIME_MASK) >> PRCM_RESET_UP_REF_TIME_SHIFT);
}

#ifdef CONFIG_ROM

static uint8_t __ext_flash_flg;

int HAL_PRCM_IsFlashSip(void)
{
	if (__ext_flash_flg) {
		return 0;
	}
	return HAL_GET_BIT(PRCM->BONDING_IO, PRCM_FLASH_SIP_EN_BIT);
}

void HAL_PRCM_SetFlashExt(uint8_t ext)
{
	__ext_flash_flg = ext;
}

uint32_t HAL_PRCM_GetHFClock(void)
{
	static const uint32_t PRCM_HOSCClock[] =
#if (CONFIG_CHIP_ARCH_VER == 2)
		{ HOSC_CLOCK_26M, HOSC_CLOCK_40M, HOSC_CLOCK_24M, HOSC_CLOCK_52M };
#elif (CONFIG_CHIP_ARCH_VER == 3)
		{ HOSC_CLOCK_26M, HOSC_CLOCK_40M, HOSC_CLOCK_24M, HOSC_CLOCK_32M };
#endif

	uint32_t val;

	val = HAL_GET_BIT_VAL(PRCM->SYS_HOSC_CTRL,
	                      PRCM_HOSC_TYPE_SHIFT,
	                      PRCM_HOSC_TYPE_VMASK);
	return PRCM_HOSCClock[val];
}

void HAL_PRCM_SetCLK32kDiv(uint32_t en, PRCM_BLE_CLK32K_DivSrcSel sel)
{
	uint32_t div;

	if (en) {
		if (sel == PRCM_BLE_CLK32K_DIV_SRC_SEL_32M) {
			div = PRCM_BLE_CLK32K_DIV_HALFCYCLE_32M;
		} else {
			div = HAL_GetHFClock() / (32 * 1000) / 2 - 1;
		}
		HAL_MODIFY_REG(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_DIV_MASK,
		               PRCM_BLE_CLK32K_DIV_VALUE(div));
		HAL_MODIFY_REG(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_DIV_SRC_SEL_MASK, sel);
		HAL_SET_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_DIV_CLK_EN_BIT);
	} else {
		HAL_CLR_BIT(PRCM->BLE_CLK32K_SWITCH0, PRCM_BLE_CLK32K_DIV_CLK_EN_BIT);
	}
}
#endif /*CONFIG_ROM */

#endif /*CONFIG_CHIP_ARCH_VER */
